package bean_factory

import (
	"gitee.com/kristas/booting-go/framework/common/util/list"
	"gitee.com/kristas/booting-go/framework/common/util/reflectx"
	. "gitee.com/kristas/booting-go/framework/core/bean"
	"reflect"
	"sort"
)

var initedBeans = list.NewConcurrentSets()
var processors []Processor
var finishProcessors []Processor

func PrepareProcessors() error {
	sort.Slice(processors, func(i, j int) bool {
		return processors[i].Order() < processors[j].Order()
	})
	for _, processor := range processors {
		err := CreateBean(processor)
		if err != nil {
			return err
		}
	}
	sort.Slice(finishProcessors, func(i, j int) bool {
		return finishProcessors[i].Order() < finishProcessors[j].Order()
	})
	for _, processor := range finishProcessors {
		err := CreateBean(processor)
		if err != nil {
			return err
		}
	}
	return nil
}

func CreateBean(b interface{}) error {
	name := GetName(b)
	if initedBeans.Exists(name) {
		return nil
	}

	err := BindProperties(b)
	if err != nil {
		return err
	}
	initedBeans.Put(name)

	err = reflectx.ForEachField(b, true, func(field reflect.StructField, value reflect.Value) error {
		if _, ok := field.Tag.Lookup(InjectFieldTag); !ok {
			return nil
		}
		switch field.Type.Kind() {
		case reflect.Slice:
			for i := 0; i < value.Len(); i++ {
				err := CreateBean(value.Index(i).Interface())
				if err != nil {
					return err
				}
			}
		default:
			err := CreateBean(value.Interface())
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return err
	}
	if bean2, ok := b.(Bean); ok {
		err := buildBeanProcess(bean2)
		if err != nil {
			return err
		}
	}
	return nil
}

func buildBeanProcess(bean Bean) error {
	for _, processor := range processors {
		if processor.Filter(bean) {
			err := processor.Do(bean)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

//func CreateGenericBean(bean interface{}) error {
//	return bindBean(bean)
//}

func Destroy() error {
	for _, processor := range finishProcessors {
		beans := GetApplicationContext().GetAllBeans()
		for _, bean := range beans {
			if processor.Filter(bean) {
				err := processor.Do(bean)
				if err != nil {
					return err
				}
			}
		}
	}
	return nil
}
